/////////////////////////////////////////////////////////////////////////////
// Name:        commandeditor.cpp
// Purpose:     Command editor classes
// Author:      Julian Smart
// Modified by:
// Created:     2008-11-18
// RCS-ID:      $Id$
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

#include "wx/wx.h"

#include "wx/datstrm.h"
#include "wx/arrimpl.cpp"
#include "wx/wfstream.h"
#include "wx/mimetype.h"
#include "wx/filename.h"

#include "commandeditor/commandeditor.h"

WX_DEFINE_OBJARRAY(wxEditorCommandArray)

WX_DEFINE_OBJARRAY(wxEditorCommandsArray)

/*
 * Information about a single command (for editing or viewing)
 */

void wxEditorCommand::Init()
{
    m_enabled = true;
    m_commandType = wxEditorCommandExternal;
    // m_isDefault = false;
}

void wxEditorCommand::Copy(const wxEditorCommand& info)
{
    m_title = info.m_title;
    m_command = info.m_command;
    m_enabled = info.m_enabled;
    // m_isDefault = info.m_isDefault;
    m_commandType = info.m_commandType;
}

bool wxEditorCommand::Save(wxOutputStream& WXUNUSED(stream), wxDataOutputStream& dataStream)
{
    wxEditorCommandManager::WriteAttributeValue(wxVariant(m_title, wxT("command-title")), dataStream);
    wxEditorCommandManager::WriteAttributeValue(wxVariant(m_command, wxT("command-command")), dataStream);
    wxEditorCommandManager::WriteAttributeValue(wxVariant(m_enabled, wxT("command-enabled")), dataStream);
    wxEditorCommandManager::WriteAttributeValue(wxVariant((long) m_commandType, wxT("command-type")), dataStream);
    // wxEditorCommandManager::WriteAttributeValue(wxVariant(m_isDefault, wxT("command-is-default")), dataStream);
    wxEditorCommandManager::WriteAttributeEnd(dataStream);

    return true;
}

bool wxEditorCommand::Load(wxInputStream& WXUNUSED(stream), wxDataInputStream& dataStream)
{
    wxVariant attr;
    
    while (wxEditorCommandManager::ReadAttributeValue(attr, dataStream))
    {
        wxString name(attr.GetName());
        if (name == wxT("command-title"))
        {
            m_title = attr.GetString();
            // Compatibility
            if (m_title == wxT("<default>"))
            {
                m_title = wxEDITOR_COMMAND_DEFAULT_NAME;
                SetDefault();
            }
        }
        else if (name == wxT("command-command"))
            m_command = attr.GetString();
        else if (name == wxT("command-enabled"))
            m_enabled = attr.GetBool();
        else if (name == wxT("command-is-default"))
        {
            // compatibility
            // m_isDefault = attr.GetBool();
            if (attr.GetBool())
                m_commandType = wxEditorCommandDefault;
        }
        else if (name == wxT("command-type"))
            m_commandType = (wxEditorCommandType) attr.GetLong();
    }

    return true;
}

/*
 * Represents a particular file type, and contains editors and viewers
 */

void wxEditorCommands::Init()
{
    m_defaultViewerName = wxEDITOR_COMMAND_DEFAULT_NAME;
    m_defaultEditorName = wxEDITOR_COMMAND_DEFAULT_NAME;
    m_mediaType = wxEditorCommandMediaTypeUnknown;
}

void wxEditorCommands::Copy(const wxEditorCommands& commands)
{
    m_fileTypeName = commands.m_fileTypeName;
    m_fileTypeDescription = commands.m_fileTypeDescription;
    m_fileTypeExtensions = commands.m_fileTypeExtensions;
    m_viewers = commands.m_viewers;
    m_editors = commands.m_editors;
    m_defaultViewerName = commands.m_defaultViewerName;
    m_defaultEditorName = commands.m_defaultEditorName;
    m_fileTypeExtensionForDefaultViewing = commands.m_fileTypeExtensionForDefaultViewing;
    m_fileTypeExtensionForDefaultEditing = commands.m_fileTypeExtensionForDefaultEditing;
    m_mediaType = commands.m_mediaType;
}

// Find viewer command
wxEditorCommand* wxEditorCommands::FindViewerCommand(const wxString& title) const
{
    size_t i;
    for (i = 0; i < m_viewers.GetCount(); i++)
    {
        if (m_viewers[i].m_title == title)
            return & m_viewers[i];
    }
    return NULL;
}

wxEditorCommand* wxEditorCommands::FindViewerCommand(int i) const
{
    return & m_viewers[i];
}

wxEditorCommand* wxEditorCommands::GetDefaultViewerCommand() const
{
    return FindViewerCommand(m_defaultViewerName);
}

// Find editor command
wxEditorCommand* wxEditorCommands::FindEditorCommand(const wxString& title) const
{
    size_t i;
    for (i = 0; i < m_editors.GetCount(); i++)
    {
        if (m_editors[i].m_title == title)
            return & m_editors[i];
    }
    return NULL;
}

wxEditorCommand* wxEditorCommands::FindEditorCommand(int i) const
{
    return & m_editors[i];
}

wxEditorCommand* wxEditorCommands::GetDefaultEditorCommand() const
{
    return FindEditorCommand(m_defaultEditorName);
}

wxArrayString wxEditorCommands::GetViewerCommandNames(bool onlyEnabledCommands) const
{
    wxArrayString names;
    size_t i;
    for (i = 0; i < m_viewers.GetCount(); i++)
    {
        if (m_viewers[i].m_enabled || !onlyEnabledCommands)
            names.Add(m_viewers[i].m_title);
    }

    return names;
}

wxArrayString wxEditorCommands::GetEditorCommandNames(bool onlyEnabledCommands) const
{
    wxArrayString names;
    size_t i;
    for (i = 0; i < m_editors.GetCount(); i++)
    {
        if (m_editors[i].m_enabled || !onlyEnabledCommands)
            names.Add(m_editors[i].m_title);
    }

    return names;
}

// Initialize default editors
void wxEditorCommands::InitializeDefaults(const wxString& extForDefaultViewing, const wxString& extForDefaultEditing, bool addInternal)
{
    m_viewers.Clear();
    m_editors.Clear();
    m_defaultViewerName = wxEDITOR_COMMAND_DEFAULT_NAME;
    m_defaultEditorName = wxEDITOR_COMMAND_DEFAULT_NAME;
    m_fileTypeExtensionForDefaultViewing = extForDefaultViewing;
    m_fileTypeExtensionForDefaultEditing = extForDefaultEditing;

    wxEditorCommand cmd;
    cmd.m_title = wxEDITOR_COMMAND_DEFAULT_NAME;
    cmd.SetCommandType(wxEditorCommandDefault);
    m_viewers.Add(cmd);
    m_editors.Add(cmd);

    if (addInternal)
    {
        wxEditorCommand internalCmd;
        internalCmd.m_title = wxEDITOR_COMMAND_INTERNAL_NAME;
        internalCmd.SetCommandType(wxEditorCommandInternal);
        internalCmd.m_enabled = false;
        m_viewers.Add(internalCmd);
        m_editors.Add(internalCmd);
    }
}

bool wxEditorCommands::Save(wxOutputStream& stream, wxDataOutputStream& dataStream)
{
    wxEditorCommandManager::WriteAttributeValue(wxVariant(m_fileTypeName, wxT("file-type-name")), dataStream);
    wxEditorCommandManager::WriteAttributeValue(wxVariant(m_fileTypeDescription, wxT("file-type-description")), dataStream);
    wxEditorCommandManager::WriteAttributeValue(wxVariant(m_fileTypeExtensions, wxT("file-type-extensions")), dataStream);
    wxEditorCommandManager::WriteAttributeValue(wxVariant(m_defaultViewerName, wxT("file-type-default-viewer-name")), dataStream);
    wxEditorCommandManager::WriteAttributeValue(wxVariant(m_defaultEditorName, wxT("file-type-default-editor-name")), dataStream);
    wxEditorCommandManager::WriteAttributeValue(wxVariant(m_fileTypeExtensionForDefaultViewing, wxT("file-type-extension-for-default-viewing")), dataStream);
    wxEditorCommandManager::WriteAttributeValue(wxVariant(m_fileTypeExtensionForDefaultEditing, wxT("file-type-extension-for-default-editing")), dataStream);
    wxEditorCommandManager::WriteAttributeValue(wxVariant(m_mediaType, wxT("file-type-media-type")), dataStream);

    wxEditorCommandManager::WriteAttributeValue(wxVariant((long) m_viewers.GetCount(), wxT("file-type-viewers-count")), dataStream);
    size_t i;
    for (i = 0; i < m_viewers.GetCount(); i++)
    {
        m_viewers[i].Save(stream, dataStream);
    }

    wxEditorCommandManager::WriteAttributeValue(wxVariant((long) m_editors.GetCount(), wxT("file-type-editors-count")), dataStream);
    for (i = 0; i < m_editors.GetCount(); i++)
    {
        m_editors[i].Save(stream, dataStream);
    }

    wxEditorCommandManager::WriteAttributeEnd(dataStream);

    return true;
}

bool wxEditorCommands::Load(wxInputStream& stream, wxDataInputStream& dataStream)
{
    m_viewers.Clear();
    m_editors.Clear();

    wxVariant attr;
    
    while (wxEditorCommandManager::ReadAttributeValue(attr, dataStream))
    {
        wxString name(attr.GetName());
        if (name == wxT("file-type-name"))
            m_fileTypeName = attr.GetString();
        else if (name == wxT("file-type-description"))
            m_fileTypeDescription = attr.GetString();
        else if (name == wxT("file-type-extensions"))
            m_fileTypeExtensions = attr.GetString();
        else if (name == wxT("file-type-default-viewer-name"))
            m_defaultViewerName = attr.GetString();
        else if (name == wxT("file-type-default-editor-name"))
            m_defaultEditorName = attr.GetString();
        else if (name == wxT("file-type-extension-for-default-viewing"))
            m_fileTypeExtensionForDefaultViewing = attr.GetString();
        else if (name == wxT("file-type-extension-for-default-editing"))
            m_fileTypeExtensionForDefaultEditing = attr.GetString();
        else if (name == wxT("file-type-media-type"))
            m_mediaType = (int) attr.GetLong();

        else if (name == wxT("file-type-viewers-count"))
        {
            int count = attr.GetLong();
            int i;
            for (i = 0; i < count; i++)
            {
                wxEditorCommand cmd;
                cmd.Load(stream, dataStream);

                m_viewers.Add(cmd);
            }
        }

        else if (name == wxT("file-type-editors-count"))
        {
            int count = attr.GetLong();
            int i;
            for (i = 0; i < count; i++)
            {
                wxEditorCommand cmd;
                cmd.Load(stream, dataStream);

                m_editors.Add(cmd);
            }
        }
    }

    return true;
}

/*
 * Manages the database of commands
 */

wxEditorCommandManager::~wxEditorCommandManager()
{
    // Will this be a problem if we clean up wxWidgets before deleting?
    if (m_currentMenu)
        delete m_currentMenu;
}

void wxEditorCommandManager::Init()
{
    m_currentSelection = -1; 
    m_currentlyEditing = false;
    m_currentMenu = NULL;
    m_startMenuId = -1;
    m_endMenuId = -1;
}

void wxEditorCommandManager::Copy(const wxEditorCommandManager& manager)
{
    m_fileTypes = manager.m_fileTypes;
}

// Finds the file type for the given name
wxEditorCommands* wxEditorCommandManager::GetFileType(const wxString& name, int flags)
{
    if (name.IsEmpty())
        return NULL;

    wxString name1(name.Lower());

    size_t i;
    for (i = 0; i < m_fileTypes.GetCount(); i++)
    {
        if (flags == wxEDITOR_COMMAND_BY_EXTENSION)
        {
            if (m_fileTypes[i].m_fileTypeExtensions.Find(name1) != wxNOT_FOUND)
                return & m_fileTypes[i];
        }
        else // if (flags == wxEDITOR_COMMAND_BY_TYPE)
        {
            if (m_fileTypes[i].m_fileTypeName.Lower() == name1)
                return & m_fileTypes[i];
        }
    }
    return NULL;
}

// Adds a file type
void wxEditorCommandManager::AddFileType(const wxEditorCommands& fileType)
{
    m_fileTypes.Add(fileType);
}

// Deletes a file type
bool wxEditorCommandManager::DeleteFileType(int i)
{
    m_fileTypes.RemoveAt(i);
    return true;
}

// Finds the current default viewer command for the given type
wxEditorCommand* wxEditorCommandManager::GetDefaultViewer(const wxString& name, int flags)
{
    wxEditorCommands* cmds = GetFileType(name, flags);
    if (cmds)
        return cmds->GetDefaultViewerCommand();
    else
        return NULL;
}

// Finds the current default editor command for the given type
wxEditorCommand* wxEditorCommandManager::GetDefaultEditor(const wxString& typeName, int flags)
{
    wxEditorCommands* cmds = GetFileType(typeName, flags);
    if (cmds)
        return cmds->GetDefaultEditorCommand();
    else
        return NULL;
}

// Save
bool wxEditorCommandManager::Save(wxOutputStream& stream, wxDataOutputStream& dataStream)
{
    wxEditorCommandManager::WriteAttributeValue(wxVariant((long) m_fileTypes.GetCount(), wxT("file-type-count")), dataStream);
    size_t i;
    for (i = 0; i < m_fileTypes.GetCount(); i++)
    {
        m_fileTypes[i].Save(stream, dataStream);
    }

    wxEditorCommandManager::WriteAttributeEnd(dataStream);

    return true;
}

bool wxEditorCommandManager::Save(const wxString& filename)
{
    wxFileOutputStream stream(filename);
    if (stream.IsOk())
    {
        wxDataOutputStream dataStream(stream);
        return Save(stream, dataStream);
    }
    else
        return false;
}

// Load
bool wxEditorCommandManager::Load(wxInputStream& stream, wxDataInputStream& dataStream)
{
    m_fileTypes.Clear();

    wxVariant attr;
    
    while (wxEditorCommandManager::ReadAttributeValue(attr, dataStream))
    {
        wxString name(attr.GetName());
        if (name == wxT("file-type-count"))
        {
            int count = attr.GetLong();
            int i;
            for (i = 0; i < count; i++)
            {
                wxEditorCommands cmds;
                cmds.Load(stream, dataStream);

                m_fileTypes.Add(cmds);
            }
        }
    }

    return true;
}

bool wxEditorCommandManager::Load(const wxString& filename)
{
    wxFileInputStream stream(filename);
    if (stream.IsOk())
    {
        wxDataInputStream dataStream(stream);
        return Load(stream, dataStream);
    }
    else
        return false;
}

// Launches file
bool wxEditorCommandManager::LaunchFile(const wxEditorCommandInfo& info)
{
    wxString filename = info.m_filename;
    wxString defaultExtForCmd;
    if (info.m_editing)
        defaultExtForCmd = info.m_commands->GetFileTypeExtensionForDefaultEditing();
    else
        defaultExtForCmd = info.m_commands->GetFileTypeExtensionForDefaultViewing();
    return LaunchFile(filename, * info.m_command, defaultExtForCmd);
}

// Launches file
bool wxEditorCommandManager::LaunchFile(const wxString& filename, const wxEditorCommand& cmd, const wxString& defaultExtForCmd)
{
    if (cmd.IsDefault())
    {
#if !defined(__WXMAC__)
        if (!defaultExtForCmd.IsEmpty())
        {
            wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(defaultExtForCmd);
            if ( ft )
            {
                wxString cmd;
                wxString params(filename);
                bool ok = ft->GetOpenCommand(&cmd, wxFileType::MessageParameters(params));
                delete ft;
                
                if (ok)
                {
                    ok = (wxExecute(cmd, wxEXEC_ASYNC) != 0);
                    if (ok)
                        return true;
                }
            }
        }
#endif

        // Failed to use the specified extension - just launch the file.
        // return stExecuteFile(filename);
        return DefaultLaunchFile(filename);
    }
    else if (cmd.IsInternal())
    {

        // TODO
        wxMessageBox(wxT("Internal commands currently unimplemented."));
        return false;
    }
    else
    {
        // TODO: keyword substitution?
        wxString cmdString = cmd.m_command + wxT(" \"") + filename + wxT("\"");
        return wxExecute(cmdString, false) != 0;
    }

    return false;
}

// Launches viewer command for this file
bool wxEditorCommandManager::LaunchViewer(const wxString& filename, const wxString& typeName, const wxString& viewerCmdName, int flags)
{
    wxEditorCommands* cmds = GetFileType(typeName, flags);
    if (!cmds)
        return false;

    wxEditorCommand* cmd = NULL;
    if (viewerCmdName.IsEmpty())
        cmd = cmds->GetDefaultViewerCommand();
    else
        cmd = cmds->FindViewerCommand(viewerCmdName);

    if (cmd)
        return LaunchFile(filename, * cmd, cmds->GetFileTypeExtensionForDefaultViewing());
    else
        return false;
}

// Launches editor command for this file
bool wxEditorCommandManager::LaunchEditor(const wxString& filename, const wxString& typeName, const wxString& editorCmdName, int flags)
{
    wxEditorCommands* cmds = GetFileType(typeName, flags);
    if (!cmds)
        return false;

    wxEditorCommand* cmd = NULL;
    if (editorCmdName.IsEmpty())
        cmd = cmds->GetDefaultEditorCommand();
    else
        cmd = cmds->FindEditorCommand(editorCmdName);

    if (cmd)
        return LaunchFile(filename, * cmd, cmds->GetFileTypeExtensionForDefaultEditing());
    else
        return false;
}

// Launch file either immediately or after user chooses from a menu
bool wxEditorCommandManager::LaunchFile(wxWindow* win, const wxString& filename, const wxString& fileType, bool editing, int flags)
{
    wxArrayString filenames, fileTypes;
    filenames.Add(filename);
    fileTypes.Add(fileType);

    return LaunchFile(win, filenames, fileTypes, editing, flags);
}

#if 0
// Launch file either immediately or after user chooses from a menu
bool wxEditorCommandManager::LaunchFile(wxWindow* win, const wxArrayString& filenames, const wxArrayString& fileTypes, bool editing, int flags)
{
    wxASSERT_MSG(m_startMenuId != -1, wxT("Please call SetMenuIdRange first."));

    m_currentFileTypes.Clear();
    m_currentFilenames.Clear();
    m_currentCmdChoices.Clear();
    m_currentlyEditing = editing;

    wxArrayString displayNames;

    size_t j;
    for (j = 0; j < fileTypes.GetCount(); j++)
    {
        wxEditorCommands* cmds = GetFileType(fileTypes[j], flags);
        if (cmds)
        {
            wxArrayString cmdNames;
            if (editing)
                cmdNames = cmds->GetEditorCommandNames();
            else
                cmdNames = cmds->GetViewerCommandNames();

            wxArrayString cmdDisplayNames = cmdNames;

            if (fileTypes.GetCount() > 1)
            {
                // Disambiguate the command name using the filename
                wxString filename(wxFileNameFromPath(filenames[j]));

                size_t c;
                for (c = 0; c < cmdDisplayNames.GetCount(); c++)
                {
                    cmdDisplayNames[c] = filename + wxT(": ") + cmdDisplayNames[c];
                }
            }

            size_t k;
            for (k = 0; k < cmdNames.GetCount(); k++)
            {
                m_currentCmdChoices.Add(cmdNames[k]);
                m_currentFilenames.Add(filenames[j]);
                // m_currentFileTypes.Add(fileTypes[j]);
                m_currentFileTypes.Add(cmds->GetFileTypeName());

                displayNames.Add(cmdDisplayNames[k]);
            }
        }
    }

    if (m_currentCmdChoices.GetCount() > 1)
    {
        if (m_currentMenu)
            delete m_currentMenu;
        m_currentMenu = new wxMenu;
        size_t i;
        for (i = 0; i < displayNames.GetCount(); i++)
        {
            m_currentMenu->Append(m_startMenuId + i, displayNames[i]);
        }

        SetSelection(-1);
        
        win->PopupMenu(m_currentMenu);

        int sel = GetSelection();
        if (sel != -1)
        {
            SetSelection(-1);

            wxString cmd = m_currentCmdChoices[sel];
            wxString filename = m_currentFilenames[sel];
            wxString fileType = m_currentFileTypes[sel];
            
            if (!cmd.IsEmpty())
            {
                if (editing)
                {
                    return LaunchEditor(filename, fileType, cmd);
                }
                else
                {
                    return LaunchViewer(filename, fileType, cmd);
                }
            }
            return false;
        }

        return false;
    }

    if (m_currentCmdChoices.GetCount() == 1)
    {
        if (editing)
            return LaunchEditor(m_currentFilenames[0], m_currentFileTypes[0], m_currentCmdChoices[0]);
        else
            return LaunchViewer(m_currentFilenames[0], m_currentFileTypes[0], m_currentCmdChoices[0]);
    }

    // Last resort - just execute the file
    return stExecuteFile(filenames[0]);
}
#endif

// Launch file either immediately or after user chooses from a menu
bool wxEditorCommandManager::LaunchFile(wxWindow* win, const wxArrayString& filenames, const wxArrayString& fileTypes, bool editing, int flags)
{
    wxASSERT_MSG(m_startMenuId != -1, wxT("Please call SetMenuIdRange first."));

    wxEditorCommandInfo info;

    if (GetCommand(win, filenames, fileTypes, editing, & info, flags) == wxEDITOR_COMMAND_SUCCESS)
    {
        if (editing)
            return LaunchFile(info);
        else
            return LaunchFile(info);
    }
    else
        return false;
}

// Get the command, possibly prompting via a menu
wxEditorCommandGetCommandStatus wxEditorCommandManager::GetCommand(wxWindow* win, const wxArrayString& filenames, const wxArrayString& fileTypes, bool editing, wxEditorCommandInfo* info, int flags)
{
    wxASSERT_MSG(m_startMenuId != -1, wxT("Please call SetMenuIdRange first."));

    m_currentFileTypes.Clear();
    m_currentFilenames.Clear();
    m_currentCmdChoices.Clear();
    m_currentlyEditing = editing;

    wxArrayString displayNames;

    bool ok = false;

    size_t j;
    for (j = 0; j < fileTypes.GetCount(); j++)
    {
        wxEditorCommands* cmds = GetFileType(fileTypes[j], flags);
        if (!cmds)
            cmds = GetFileType(wxFileName(filenames[j]).GetExt(), wxEDITOR_COMMAND_BY_EXTENSION);
        if (cmds)
        {
            wxArrayString cmdNames;
            if (editing)
                cmdNames = cmds->GetEditorCommandNames();
            else
                cmdNames = cmds->GetViewerCommandNames();

            wxArrayString cmdDisplayNames = cmdNames;

            if (fileTypes.GetCount() > 1)
            {
                // Disambiguate the command name using the filename
                wxString filename(wxFileNameFromPath(filenames[j]));

                size_t c;
                for (c = 0; c < cmdDisplayNames.GetCount(); c++)
                {
                    cmdDisplayNames[c] = filename + wxT(": ") + cmdDisplayNames[c];
                }
            }

            size_t k;
            for (k = 0; k < cmdNames.GetCount(); k++)
            {
                m_currentCmdChoices.Add(cmdNames[k]);
                m_currentFilenames.Add(filenames[j]);
                // m_currentFileTypes.Add(fileTypes[j]);
                m_currentFileTypes.Add(cmds->GetFileTypeName());

                displayNames.Add(cmdDisplayNames[k]);
            }
        }
    }

    if (m_currentCmdChoices.GetCount() > 1)
    {
        if (m_currentMenu)
            delete m_currentMenu;
        m_currentMenu = new wxMenu;
        size_t i;
        for (i = 0; i < displayNames.GetCount(); i++)
        {
            m_currentMenu->Append(m_startMenuId + i, displayNames[i]);
        }

        SetSelection(-1);
        
        win->PopupMenu(m_currentMenu);

        int sel = GetSelection();
        if (sel != -1)
        {
            SetSelection(-1);

            wxString cmd = m_currentCmdChoices[sel];
            wxString filename = m_currentFilenames[sel];
            wxString fileType = m_currentFileTypes[sel];
            
            if (!cmd.IsEmpty())
            {
                info->m_commandName = cmd;
                info->m_filename = filename;
                info->m_fileType = fileType;
                info->m_editing = editing;

                ok = true;
            }
            else
                return wxEDITOR_COMMAND_CANCELLED;
        }
        else
            return wxEDITOR_COMMAND_CANCELLED;
    }

    if (m_currentCmdChoices.GetCount() == 1)
    {
        info->m_commandName = m_currentCmdChoices[0];
        info->m_filename = m_currentFilenames[0];
        info->m_fileType = m_currentFileTypes[0];
        info->m_editing = editing;
        ok = true;
    }

    if (ok)
    {
        if (editing)
        {
            wxEditorCommands* cmds = GetFileType(info->m_fileType, wxEDITOR_COMMAND_BY_TYPE);
            if (!cmds)
                return wxEDITOR_COMMAND_FAILURE;
            
            wxEditorCommand* cmd = NULL;
            if (info->m_commandName.IsEmpty())
                cmd = cmds->GetDefaultEditorCommand();
            else
                cmd = cmds->FindEditorCommand(info->m_commandName);
            
            if (cmd)
            {
                info->m_command = cmd;
                info->m_commands = cmds;

                return wxEDITOR_COMMAND_SUCCESS;
            }
            else
                return wxEDITOR_COMMAND_FAILURE;
        }
        else
        {
            wxEditorCommands* cmds = GetFileType(info->m_fileType, wxEDITOR_COMMAND_BY_TYPE);
            if (!cmds)
                return wxEDITOR_COMMAND_FAILURE;
            
            wxEditorCommand* cmd = NULL;
            if (info->m_commandName.IsEmpty())
                cmd = cmds->GetDefaultViewerCommand();
            else
                cmd = cmds->FindViewerCommand(info->m_commandName);
            
            if (cmd)
            {
                info->m_command = cmd;
                info->m_commands = cmds;

                return wxEDITOR_COMMAND_SUCCESS;
            }
            else
                return wxEDITOR_COMMAND_FAILURE;
        }
    }
    else
        return wxEDITOR_COMMAND_FAILURE;
}

// Get the command, possibly prompting via a menu (single filename/type)
wxEditorCommandGetCommandStatus wxEditorCommandManager::GetCommand(wxWindow* win, const wxString& filename, const wxString& fileType, bool editing, wxEditorCommandInfo* info, int flags)
{
    wxArrayString filenames, fileTypes;
    filenames.Add(filename);
    fileTypes.Add(fileType);

    return GetCommand(win, filenames, fileTypes, editing, info, flags);
}


// Popup menu event handler
bool wxEditorCommandManager::DoMenuCommand(wxCommandEvent& event)
{
#if 1
    SetSelection(event.GetId() - m_startMenuId);
    return true;
#else
    wxString cmd = m_currentCmdChoices[event.GetId() - m_startMenuId];
    wxString filename = m_currentFilenames[event.GetId() - m_startMenuId];
    wxString fileType = m_currentFileTypes[event.GetId() - m_startMenuId];

    if (!cmd.IsEmpty())
    {
        if (m_currentlyEditing)
        {
            return LaunchEditor(filename, fileType, cmd);
        }
        else
        {
            return LaunchViewer(filename, fileType, cmd);
        }
    }
    return false;
#endif
}

// Types for document I/O
#define wxEDITOR_COMMAND_TYPE_UNKNOWN  0
#define wxEDITOR_COMMAND_TYPE_LONG     1
#define wxEDITOR_COMMAND_TYPE_DOUBLE   2
#define wxEDITOR_COMMAND_TYPE_STRING   3
#define wxEDITOR_COMMAND_TYPE_BOOL     4

// Read an attribute name/value pair
// Returns false if the attribute name "END" was encountered
bool wxEditorCommandManager::ReadAttributeValue(wxVariant& variant, wxDataInputStream& dataStream)
{
    // Attributes are stored as attribute name, attribute type, attribute value
    // wxVariant values are "string", "bool", "stringlist", "list", "double", "long"

    wxString name;
    int type = wxEDITOR_COMMAND_TYPE_UNKNOWN;

    dataStream >> name;
    dataStream >> type;

    switch (type)
    {
        case wxEDITOR_COMMAND_TYPE_STRING:
        {
            wxString value = dataStream.ReadString();
            // dataStream >> value;
            variant = wxVariant(value, name);
            break;
        }
        case wxEDITOR_COMMAND_TYPE_LONG:
        {
            int value;
            dataStream >> value;
            variant = wxVariant((long) value, name);
            break;
        }
        case wxEDITOR_COMMAND_TYPE_BOOL:
        {
            int value;
            dataStream >> value;
            variant = wxVariant((bool) (value != 0), name);
            break;
        }
        case wxEDITOR_COMMAND_TYPE_DOUBLE:
        {
            double value;
            dataStream >> value;
            variant = wxVariant(value, name);
            break;
        }
        default:
        {
            wxASSERT_MSG(false, _("Unknown type when reading attribute/value"));
            return false;
            break;
        }
    }
    if (name == wxT("END"))
        return false;
    else
        return true;
}

// Writes an attribute name/value pair
bool wxEditorCommandManager::WriteAttributeValue(const wxVariant& variant, wxDataOutputStream& dataStream)
{
    int type = wxEDITOR_COMMAND_TYPE_UNKNOWN;
    wxString strType(variant.GetType());
    if (strType == wxT("string"))
    {
        type = wxEDITOR_COMMAND_TYPE_STRING;
        dataStream << variant.GetName();
        dataStream << type;
        dataStream << variant.GetString();
    }
    else if (strType == wxT("long"))
    {
        type = wxEDITOR_COMMAND_TYPE_LONG;
        dataStream << variant.GetName();
        dataStream << type;
#if 0 // def __WXMSW__
        dataStream << (long) variant.GetLong();
#else
        // TODO: see if it's ok to have this under MSW (i.e.
        // is compatible with old files)
        dataStream << (int) variant.GetLong();
#endif
    }
    else if (strType == wxT("bool"))
    {
        type = wxEDITOR_COMMAND_TYPE_BOOL;
        dataStream << variant.GetName();
        dataStream << type;
        dataStream << (int) variant.GetBool();
    }
    else if (strType == wxT("double"))
    {
        type = wxEDITOR_COMMAND_TYPE_DOUBLE;
        dataStream << variant.GetName();
        dataStream << type;
        dataStream << (double) variant.GetReal();
    }
    else
    {
        wxASSERT_MSG(false, _("Unrecognised type"));
        return false;
    }
    return true;
}

bool wxEditorCommandManager::WriteAttributeValue(const wxString& value, const wxString& name, wxDataOutputStream& dataStream)
{
    return WriteAttributeValue(wxVariant(value, name), dataStream);
}

bool wxEditorCommandManager::WriteAttributeValue(long value, const wxString& name, wxDataOutputStream& dataStream)
{
    return WriteAttributeValue(wxVariant(value, name), dataStream);
}

bool wxEditorCommandManager::WriteAttributeValue(int value, const wxString& name, wxDataOutputStream& dataStream)
{
    return WriteAttributeValue(wxVariant((long) value, name), dataStream);
}

bool wxEditorCommandManager::WriteAttributeValue(double value, const wxString& name, wxDataOutputStream& dataStream)
{
    return WriteAttributeValue(wxVariant(value, name), dataStream);
}

bool wxEditorCommandManager::WriteAttributeValue(bool value, const wxString& name, wxDataOutputStream& dataStream)
{
    return WriteAttributeValue(wxVariant(value, name), dataStream);
}

// Write end of attributes marker
bool wxEditorCommandManager::WriteAttributeEnd(wxDataOutputStream& dataStream)
{
    return WriteAttributeValue(wxVariant(wxT("END"), wxT("END")), dataStream);
}

// Clear
void wxEditorCommandManager::Clear()
{
    m_fileTypes.Clear();
    if (m_currentMenu)
    {
        delete m_currentMenu;
        m_currentMenu = NULL;
    }
}

